import logging
import pickle
import cPickle
from google.appengine.api import users
from google.appengine.api import memcache
from google.appengine.api import mail
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
import os
import re
import oauth
from django.utils import simplejson
import sys
import jsmin
import base64
import hashlib
import urllib
import traceback
#sys.path.append('pycrypto-2.0.1')
from Crypto.PublicKey import RSA
from Crypto.Util import number
from astoundlearning_app.models import CustomUser
from django.template.loader import get_template
from django.template import Context
import pprint
import settings
import logging
from opensocial import *

# http://code.google.com/appengine/docs/python/runtime.html#App_Caching
class Helper:
    __shared_state = {}
    def __init__(self,request):
        if ('combinedCSS' not in self.__shared_state):
            self.__shared_state['siteName'] = 'AstoundLearning'
            self.__shared_state['host'] = os.environ['HTTP_HOST']
            self.__shared_state['combinedCSS'] = self.combineCSS(self.__shared_state)
            self.__shared_state['combinedCSSForFacebook'] = self.combineCSSForFacebook(self.__shared_state)
            self.__shared_state['jsWithoutSocial'] = self.combineJavascriptWithoutSocial(self.__shared_state)
        self.__dict__ = self.__shared_state
        self.request = request;

    def add_facebook_instance_to_the_request_object(self):
        pass
        #fb = facebook.FacebookMiddleware(api_key=settings.FACEBOOK_API_KEY, secret_key=settings.FACEBOOK_SECRET_KEY)
        #fb.process_request(self.request)
        #fb2 = self.request.facebook
        #fb2.check_session(self.request)

    def require_facebook_login(self, next=None):
        fb = self.request.facebook
        if not fb.check_session(self.request):
            return fb.redirect(fb.get_login_url(next=next))

    def save_facebook_authentication_data_into_session(self,response):
        fb = facebook.FacebookMiddleware()
        fb.process_response(self.request,response)

    def get_open_social_container_for_google_friend_connect(self):
        config = ContainerConfig(
                oauth_consumer_key=settings.GOOGLE_FRIEND_CONNECT_CONSUMER_KEY,
                oauth_consumer_secret=settings.GOOGLE_FRIEND_CONNECT_CONSUMER_SECRET,
                server_rest_base='http://www.google.com/friendconnect/api/',
                security_token=self.request.COOKIES['fcauth' + settings.GOOGLE_FRIEND_CONNECT_SITE_ID],
                security_token_param='fcauth'
        )
        return ContainerContext(config)

    def prepareContext(self):
        context = {}
        context['logout_url'] = users.create_logout_url(self.request.build_absolute_uri('/'))
        context['login_url'] = users.create_login_url(self.request.build_absolute_uri())
        context['login_url_home'] = users.create_login_url('/')
        context['user'] = self.get_current_user()
        context['applicationName'] = context['siteName'] = self.__dict__['siteName']
        context['host'] = self.__dict__['host']
        url = 'http://' + os.environ['HTTP_HOST']
        if ((os.environ.has_key('HTTPS')) and (os.environ['HTTPS'] == 'on')):
            url = 'https://' + os.environ['HTTP_HOST']
        context['baseURL'] = url
        context['combinedCSS'] = self.__dict__['combinedCSS']
        context['combinedCSSForFacebook'] = self.__dict__['combinedCSSForFacebook']
        context['jsWithoutSocial'] = self.__dict__['jsWithoutSocial']
        return context

    def deleteCache(self,ck):
        memcache.delete(ck)

    def getCache(self,ck):
        instance = memcache.get(ck)
        if instance is not None:
            instance = pickle.loads(instance)
        else:
            instance = db.get(ck)
            memcache.set(ck,pickle.dumps(instance))
        return instance

    def isSocial(self):
        return self.social

    def get_current_user(self):
        if (self._isValidSignature()):
            self.social = True
            viewerID = self.request.REQUEST.get('opensocial_viewer_id')
            consumerKey = self.request.REQUEST.get('oauth_consumer_key')
            user = CustomUser.gql("WHERE viewerID = :viewerID AND consumerKey = :consumerKey", viewerID = viewerID, consumerKey = consumerKey).get()
            if (not user):
                user = CustomUser(isSocial = True, viewerID = viewerID, consumerKey = consumerKey)
                user.put()
                return user
        else:
            self.social = False
            user = users.get_current_user();
            if (user is None):
                request = self.request
                if (getattr(request, 'facebook', None)) and (request.facebook.uid is None) and ('facebook_session_key' in request.session):
                    request.facebook.session_key = request.session['facebook_session_key']
                    request.facebook.uid = request.session['facebook_user_id']

                if (getattr(request, 'facebook', None)) and (request.facebook.uid):
                    uid = request.facebook.uid
                    request.session['facebook_session_key'] = request.facebook.session_key
                    request.session['facebook_user_id'] = request.facebook.uid
                    consumerKey = 'facebook'
                    user = CustomUser.gql("WHERE viewerID = :viewerID AND consumerKey = :consumerKey", viewerID = uid, consumerKey = consumerKey).get()
                    if (not user):
                        name = request.facebook.users.getInfo([uid],['name'])[0]['name']
                        user = CustomUser(isSocial = False, viewerID = uid, consumerKey = consumerKey, name = name)
                        user.put()
                        return user
                if ('gfcUserID' in request.session):
                    user = CustomUser.gql("WHERE gfcUserID = :gfcUserID", gfcUserID = request.session['gfcUserID']).get()
                    return user
                elif ('fcauth' + settings.GOOGLE_FRIEND_CONNECT_SITE_ID in request.COOKIES):
                    cookieContent = request.COOKIES['fcauth' + settings.GOOGLE_FRIEND_CONNECT_SITE_ID]
                    op = urllib.urlopen('http://www.google.com/friendconnect/api/people/@viewer/@self?fcauth=' + cookieContent)
                    viewer = simplejson.load(op)
                    viewer = viewer['entry']
                    request.session['gfcUserID'] = gfcUserID = viewer['id']
                    user = CustomUser(gfcUserID = gfcUserID, name = viewer['displayName'])
                    user.put()
                    return user
                return None
            user = CustomUser.gql("WHERE googleAccount = :googleAccount", googleAccount = users.get_current_user()).get()
            if (user is None):
                user = users.get_current_user()
                if (user is None):
                    return None
                nickname = user.nickname()
                email = user.email()
                if (nickname == email):
                    nickname = ''
                user = CustomUser(googleAccount = user, name = nickname, email = email)
                user.put()
            return user

    def convertGoogleAccountToCustomUser(self,googleAccount):
        user = CustomUser.gql("WHERE googleAccount = :googleAccount", googleAccount = googleAccount).get()
        if (not user):
            user = CustomUser(googleAccount = googleAccount)
            user.put()
        return user

    def _isValidSignature(self):
        # oauth_consumer_key parameter indicates the container from which the request is being sent.
        # oauth_signature_method indicates the method used to sign the request
        # For RSA-SHA1, xoauth_signature_publickey contains the name of the public key used to sign the request
        # For HMAC-SHA1, you will need to establish a secret key between you application and the container.
        # For RSA-SHA1, you will need to obtain the certificate referenced by xoauth_signature_publickey.  See
        # https://opensocialresources.appspot.com/certificates
        # Certificates should not be fetched each time you want to validate parameters.  Implements a server side
        # key cache indexed on the value of xoauth_signature_publickey, oauth_consumer_key, and oauth_signature_method
        # Construct a RSA.public object
        consumerKey = self.request.REQUEST.get('oauth_consumer_key')
        signatureMethod = self.request.REQUEST.get('oauth_signature_method')
        if (not signatureMethod) or (signatureMethod == ''):
            return False

        if (signatureMethod == 'RSA-SHA1'):
            exponent = 65537
            publicKeys = {}
            publicKeys['www.google.com']  = """0x00d0515eee9087c88b16e890738d18c5bdf9e77413d5f89bdf48f2ea4f429de202da88bd6b3b5c26c06c6ab3407d6a5fd634d21ad0e514508fc388ded46242cfca7f319639dbcba48939a17a5d4f9f2d838165621e5f6e1228568567e06bed4a32a6245b2833c351b442472f569677ef9d5f39108c4b0d7015f042f7c36f46276d"""
            publicKeys['orkut.com'] = """0x00b1e057678343866db89d7dec251899261bf2f5e0d95f5d868f81d600c9a101c9e6da20606290228308551ed3acf9921421dcd01ef1de35dd3275cd4983c7be0be325ce8dfc3af6860f7ab0bf32742cd9fb2fcd1cd1756bbc400b743f73acefb45d26694caf4f26b9765b9f65665245524de957e8c547c358781fdfb68ec056d1"""
            publicKeys['hi5.com'] = """0x009981dad8b0409e18efd8b2b0df68efef23232aefe0d87409889b3c20df9c27b62dc3af707a912e8e791881008513bdd6575f75e2b491715650c51ff3db4629a6d1ddde42f49f5b7fb4c48845f42dc4f50163e8b581cfa6c1961e69d29497ca0019f405d1d4e30cda50d147d870429d58563cc99378585b6a83e7dd5ab91390bf"""
            public_key_str = publicKeys[consumerKey]
            public_key_long = long(public_key_str, 16)
            public_key = RSA.construct((public_key_long, exponent))

            # Rebuild the message host locally
            # I am not sure what the difference between webapp / WebOb and django is, but the webapp / WebOb version that we had
            # before moving over to django works without having to copy each individual parameter over to a new dictionary
            url = self.request.build_absolute_uri()
            params = {}
            request = self.request.REQUEST
            for k,v in request.items():
                params[k] = v
                params.pop('oauth_signature')

                oauth_signature = self.request.REQUEST.get('oauth_signature')
                oauth_request = oauth.OAuthRequest(http_method = self.request.method,
                        http_url=self.request.build_absolute_uri(),
                        parameters=params)
                message = '&'.join((oauth.escape(oauth_request.get_normalized_http_method()),
                    oauth.escape(oauth_request.get_normalized_http_url()),
                    oauth.escape(oauth_request.get_normalized_parameters()),))
                local_hash = hashlib.sha1(message).digest()

                # Apply the public key to the signature from the remote host
                sig = base64.decodestring(urllib.unquote(oauth_signature))
                remote_hash = public_key.encrypt(sig,'')[0][-20:]

                # Verify that the locally-built value matches the value from the remote server.
                if local_hash == remote_hash:
                    return True
                else:
                    logging.debug('Either oauth_consumer_key: ' + consumerKey + ' does not exist in publicKeys or the container changed its public key' + "\n")
                    return False
        elif (signatureMethod == 'HMAC-SHA1'):
            logging.info('Unsupported oauth_signature_method ' + signatureMethod + ', oauth_consumer_key: ' + consumerKey + "\n");
            return False
        else:
            logging.info('Unsupported oauth_signature_method ' + signatureMethod + ', oauth_consumer_key: ' + consumerKey + "\n");
            return False

    def combineTemplates(self,context):
        templates = ['addEntryTemplate','listViewRowTemplate_contributor','listViewTemplate']
        templates.append('editEntryTemplate')
        templates.append('listViewTemplate_contributor')
        templates.append('studyTableTemplate')
        templates.append('addEntryTemplate')
        templates.append('contact')
        templates.append('faq')
        templates.append('ourFeedback')
        templates.append('tos')
        templates.append('help')
        templates.append('importForm')
        templates_json = {}
        for t in templates:
            template = get_template('astoundlearning_app/ajax/' + t + '.html')
            templates_json[t] = template.render(Context(context))
        return simplejson.dumps(templates_json)

    def combineCSS(self,context):
        base = os.path.dirname(__file__)
        css = file(os.path.join(base,'css/base.css')).read();
        css = css + "\n" + file(os.path.join(base,'css/jquery-ui-1.8.13/themes/smoothness/jquery-ui-1.8.13.custom.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/TinyDropdown.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/speechbubbles.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/jquery.tooltip.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/our.css')).read()
        return css

    def combineCSSForFacebook(self,context):
        base = os.path.dirname(__file__)
        css = file(os.path.join(base,'css/themes/flora/flora.tabs.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/jqModal.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/themes/flora/flora.css')).read()
        css = css + "\n" + file(os.path.join(base,'css/themes/flora/facebook.css')).read()
        return css

    def combineJavascriptWithoutSocial(self,context):
        base = os.path.dirname(__file__)
        #js = file(os.path.join(base,'javascript/jquery-1.2.6.min.js')).read();
        #js = js + file(os.path.join(base,'javascript/jquery.ui-1.5.2/ui/minified/jquery.ui.all.min.js')).read() + "\n\n"
        js = file(os.path.join(base,'javascript/jquery-1.4.4.min.js')).read();
        js = js + file(os.path.join(base,'javascript/jquery-ui-1.8.13/jquery-ui-1.8.13.custom.min.js')).read() + "\n\n"
        #js = js + file(os.path.join(base,'tinymce/jscripts/tiny_mce/tiny_mce.js')).read() + "\n\n"
        if (False):
            t = file(os.path.join(base,'javascript/plugins4jquery/blockUI.js')).read() + "\n\n"
            t = t + file(os.path.join(base,'javascript/jquery.scrollIntoView.js')).read() + "\n\n"
            t = t + file(os.path.join(base,'javascript/our.js')).read() + "\n\n"
            js = js + jsmin.jsmin(t)
        else:
            js = js + file(os.path.join(base,'javascript/plugins4jquery/blockUI.js')).read() + "\n\n"
            js = js + file(os.path.join(base,'javascript/jquery.scrollIntoView.js')).read() + "\n\n"
            js = js + file(os.path.join(base,'javascript/TinyDropdown.js')).read() + "\n\n"
            js = js + file(os.path.join(base,'javascript/speechbubbles.js')).read() + "\n\n"
            js = js + file(os.path.join(base,'javascript/our.js')).read() + "\n\n"
        js = js + 'study.templates = ' + self.combineTemplates(context) + ';' + "\n\n"
        js = js + file(os.path.join(base,'javascript/jqModal.js')).read() + "\n\n"
        js = js + file(os.path.join(base,'javascript/jqDnR.js')).read() + "\n\n"
        js = js + file(os.path.join(base,'javascript/jquery.cookie.js')).read() + "\n\n"
        js = js + file(os.path.join(base,'javascript/jquery.tooltip.js')).read() + "\n\n"
        js = js + file(os.path.join(base,'javascript/ajaxfileupload.js')).read() + "\n\n"
        js = js.decode('utf-8').encode('ascii','ignore')
        return js
